#include <dirent.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

// export PKG_CONFIG_PATH=/opt/ego/opencv/lib/pkgconfig/
// g++ -I./ -Wall -Wno-unknown-pragmas -Wfatal-errors -Wunused-but-set-variable -Wno-unused-result -Ofast -std=c++11 test_threshold.cpp `pkg-config --cflags --libs opencv` -L../cnn/obj -lcnn -L/usr/local/cuda/lib64 -lcudart -lcurand -lcublas
// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-8.0/lib64/stubs/:/opt/ego/opencv/lib:../cnn/obj:/usr/local/cuda-8.0/lib64

#ifdef __cplusplus
extern "C"{
#endif
    void init_recognition(const char *cfgfile, const char *weightfile);
    void run_recognition(float *image_data, int face_num, float *feature);
    void uninit_recognition();
#ifdef __cplusplus
}
#endif


void extract_face_feature(cv::Mat &img, float *feature)
{
    int face_count = 1;
    int face_width = 112;
    int face_height = 112;
    cv::Mat face_resized;
    cv::resize(img, face_resized, cv::Size(face_width, face_height));
    int channels = img.channels();
    float *face_data = (float *)malloc(face_count * face_width * face_height * channels *sizeof(float));
    for(int i = 0; i < face_count; i++){
        float *face_data_offset = face_data + i * face_width * face_height * channels;
        for(int k= 0; k < channels; ++k){
            for(int m = 0; m < face_height; ++m){
                for(int n = 0; n < face_width; ++n){
                    face_data_offset[k* face_width * face_height + m * face_width + n] =
                        (face_resized.at<cv::Vec3b>(m, n)[channels - k - 1] - 127.5) * 0.0078125;
                }
            }
        }
    }
    run_recognition(face_data, face_count, feature);
    free(face_data);
}

bool string_end_with(std::string const &fullString, std::string const &ending) {
    if (fullString.length() >= ending.length()) {
        return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
    } else {
        return false;
    }
}

int main(){

    init_recognition("/media/iim/disk/darknet/test/cnn/cfg/cosface_new_100.cfg", "/media/iim/disk/darknet/test/cnn/model/model.cnn.100");

    struct dirent *ent;
    DIR *dir;
    std::string image_path = "test_image";
    if ((dir = opendir(image_path.c_str())) != NULL) {
        while ((ent = readdir(dir)) != NULL) {
            std::string file_name(ent->d_name);
            if(string_end_with(file_name, ".")){
                //std::cout << "skip dir " << file_name << std::endl;
                continue;
            }
            //std::cout << "dir " << file_name << std::endl;
            struct dirent *ent_child;
            DIR *dir_child;
            std::string image_child_path = image_path + "/" + file_name;
            //std::cout << "dir " << image_child_path << std::endl;
            std::vector<float *> face_feature;
            std::vector<std::string> face_feature_path;
            if ((dir_child = opendir(image_child_path.c_str())) != NULL) {
                while ((ent_child = readdir(dir_child)) != NULL) {
                    std::string file_name_image(ent_child->d_name);
                    if(string_end_with(file_name_image, ".")){
                        //std::cout << "skip dir " << image_child_path << "/" << file_name_image << std::endl;
                        continue;
                    }
                    cv::Mat img = cv::imread(image_child_path + "/" + file_name_image);
                    //cv::imwrite("aaaa.jpg", img);
                    float *feature = (float *)malloc(512 * sizeof(float));
                    extract_face_feature(img, feature);
                    face_feature.push_back(feature);
                    face_feature_path.push_back(file_name_image);
                }
                closedir(dir_child);
            } else {
                perror ("could not open directory");
                return -1;
            }
            float min_score = 100.0f;
            int min_i = -1;
            int min_j = -1;
            float second_score = 100.0f;
            int second_i = -1;
            int second_j = -1;
            for(unsigned int i = 0; i < face_feature.size(); i++){
                float *current_feature = face_feature[i];
                for(unsigned int j = 0; j < face_feature.size(); j++){
                    if(i == j) continue;
                    float score = 0;
                    for(int m = 0; m < 512; m++){
                        score += current_feature[m] * face_feature[j][m];
                    }
                    if(score < min_score) {
                        second_score = min_score;
                        second_i = min_i;
                        second_j = min_j;
                        min_score = score;
                        min_i = i;
                        min_j = j;
                    }
                }
            }
            if(min_score < 0.65){
            std::cout << image_child_path << " min_score " << min_score << " " << face_feature_path[min_i] << " " << face_feature_path[min_j]  << std::endl;
            std::cout << image_child_path << " second_score " << second_score << " " << face_feature_path[second_i] << " " << face_feature_path[second_j]  << std::endl;
            }
            for(unsigned int i = 0; i < face_feature.size(); i++){
                free(face_feature[i]);
            }
            face_feature.clear();
            face_feature_path.clear();
            //break;
        }
        closedir(dir);
    } else {
        perror("could not open directory !");
        return -1;
    }
    return 0;
}
